package com.mentalhealth.diary.service.impl;

import com.mentalhealth.diary.dto.*;
import com.mentalhealth.diary.entity.Member;
import com.mentalhealth.diary.entity.User;
import com.mentalhealth.diary.exception.BusinessException;
import com.mentalhealth.diary.mapper.UserMapper;
import com.mentalhealth.diary.service.AuthService;
import com.mentalhealth.diary.service.MemberService;
import com.mentalhealth.diary.utils.JwtUtils;
import com.mentalhealth.diary.utils.PasswordUtils;
import com.mentalhealth.diary.vo.LoginVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Base64;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 认证服务实现类
 */
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MemberService memberService;

    @Autowired
    private JwtUtils jwtUtils;

    // Redis Key前缀常量
    private static final String SMS_CODE_REGISTER_PREFIX = "sms_code:register:";
    private static final String SMS_CODE_LOGIN_PREFIX = "sms_code:login:";
    private static final String SMS_CODE_RESET_PREFIX = "sms_code:reset:";
    private static final String SMS_CODE_BIND_PREFIX = "sms_code:bind:";
    private static final String LOGIN_ERROR_COUNT_PREFIX = "login_error:count:";
    private static final String LOGIN_LOCK_PREFIX = "login_lock:";
    private static final String DEVICE_VERIFY_PREFIX = "device_verify:";
    private static final String THIRD_LOGIN_CACHE_PREFIX = "third_login:";
    private static final String TOKEN_BLACKLIST_PREFIX = "token_blacklist:";
    
    // 常量定义
    private static final int SMS_CODE_EXPIRE = 60; // 验证码有效期60秒
    private static final int LOGIN_ERROR_LIMIT = 5; // 密码错误次数限制
    private static final int LOGIN_LOCK_TIME = 15 * 60; // 锁定时间15分钟
    private static final int DEVICE_VERIFY_EXPIRE = 30 * 60; // 设备验证缓存30分钟
    private static final int THIRD_LOGIN_EXPIRE = 10 * 60; // 第三方登录信息缓存10分钟
    private static final int REFRESH_TOKEN_EXPIRE = 7 * 24 * 60 * 60; // 刷新令牌过期时间7天

    @Override
    public void sendRegisterCode(SendCodeDTO sendCodeDTO) {
        String phone = sendCodeDTO.getPhone();
        String cacheKey = SMS_CODE_REGISTER_PREFIX + phone;

        // 检查是否在60秒内已发送过验证码
        if (redisTemplate.hasKey(cacheKey)) {
            throw new BusinessException("验证码已发送，请60秒后重试");
        }

        // 检查手机号是否已注册
        if (userMapper.selectByPhone(phone) != null) {
            throw new BusinessException("该手机号已注册，请直接登录");
        }

        // 生成4位数字验证码
        String code = generateSmsCode();
        
        // 缓存验证码（实际项目中应该调用短信发送接口）
        redisTemplate.opsForValue().set(cacheKey, code, SMS_CODE_EXPIRE, TimeUnit.SECONDS);
        
        // 这里只是模拟发送验证码，实际应该调用短信服务商的API
        System.out.println("向手机号 " + phone + " 发送注册验证码: " + code);
    }

    @Override
    public void sendLoginCode(SendCodeDTO sendCodeDTO) {
        String phone = sendCodeDTO.getPhone();
        String cacheKey = SMS_CODE_LOGIN_PREFIX + phone;

        // 检查是否在60秒内已发送过验证码
        if (redisTemplate.hasKey(cacheKey)) {
            throw new BusinessException("验证码已发送，请60秒后重试");
        }

        // 检查手机号是否已注册
        if (userMapper.selectByPhone(phone) == null) {
            throw new BusinessException("该手机号未注册，请先注册");
        }

        // 生成4位数字验证码
        String code = generateSmsCode();
        
        // 缓存验证码
        redisTemplate.opsForValue().set(cacheKey, code, SMS_CODE_EXPIRE, TimeUnit.SECONDS);
        
        // 模拟发送验证码
        System.out.println("向手机号 " + phone + " 发送登录验证码: " + code);
    }

    @Transactional
    @Override
    public LoginVO submitRegister(RegisterDTO registerDTO) {
        String phone = registerDTO.getPhone();
        String code = registerDTO.getCode();
        String password = registerDTO.getPassword();
        
        // 验证验证码
        String cacheKey = SMS_CODE_REGISTER_PREFIX + phone;
        String cachedCode = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cachedCode == null) {
            throw new BusinessException("验证码已过期，请重新获取");
        }
        if (!code.equals(cachedCode)) {
            throw new BusinessException("验证码错误，请重新输入");
        }

        // 检查手机号是否已注册
        if (userMapper.selectByPhone(phone) != null) {
            throw new BusinessException("该手机号已注册，请直接登录");
        }

        // 密码强度校验（这里只是提示，不强制）
        int passwordStrength = PasswordUtils.checkPasswordStrength(password);
        if (passwordStrength == 0) {
            System.out.println("密码强度较弱: " + PasswordUtils.getStrengthMessage(passwordStrength));
        }

        // 创建用户
        User user = new User();
        user.setPhone(phone);
        user.setPassword(PasswordUtils.encode(password));
        user.setNickname("用户" + phone.substring(phone.length() - 4));
        user.setAvatar("default_avatar.png");
        user.setStatus(1);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        userMapper.insert(user);

        // 开通3天体验会员
        Member member = memberService.openExperienceMember(user.getId());

        // 清除验证码
        redisTemplate.delete(cacheKey);

        // 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId().toString());

        // 构建返回结果
        return buildLoginVO(user, member, token);
    }

    @Override
    public LoginVO phoneLogin(PhoneLoginDTO phoneLoginDTO) {
        String phone = phoneLoginDTO.getPhone();
        String code = phoneLoginDTO.getCode();
        String deviceInfo = phoneLoginDTO.getDeviceInfo();
        
        // 验证验证码
        String cacheKey = SMS_CODE_LOGIN_PREFIX + phone;
        String cachedCode = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cachedCode == null) {
            throw new BusinessException("验证码已过期，请重新获取");
        }
        if (!code.equals(cachedCode)) {
            throw new BusinessException("验证码错误，请重新输入");
        }

        // 查询用户
        User user = userMapper.selectByPhone(phone);
        if (user == null) {
            throw new BusinessException("用户不存在，请先注册");
        }

        // 检查设备信息，进行异地/新设备检测
        if (deviceInfo != null) {
            checkDeviceVerification(user.getId(), deviceInfo);
        }

        // 清除验证码
        redisTemplate.delete(cacheKey);

        // 获取会员信息
        Member member = memberService.getUserMember(user.getId());
        
        // 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId().toString());

        // 构建返回结果
        return buildLoginVO(user, member, token);
    }

    @Override
    public LoginVO thirdLogin(ThirdLoginDTO thirdLoginDTO) {
        String platform = thirdLoginDTO.getPlatform();
        String code = thirdLoginDTO.getCode();
        
        // 验证第三方平台类型
        if (!"WECHAT".equals(platform) && !"ALIPAY".equals(platform)) {
            throw new BusinessException("不支持的第三方登录平台");
        }

        // 模拟第三方登录授权验证
        String thirdOpenId = simulateThirdAuth(code, platform);
        if (thirdOpenId == null) {
            throw new BusinessException("第三方登录授权失败");
        }

        // 生成唯一的第三方登录标识
        String thirdKey = platform + ":" + thirdOpenId;
        
        // 查询用户是否已绑定第三方账号
        User user = findUserByThirdKey(thirdKey);
        
        // 如果用户不存在，创建新用户
        if (user == null) {
            user = createUserByThirdLogin(platform, thirdOpenId);
            // 为新用户开通体验会员
            memberService.openExperienceMember(user.getId());
        }

        // 获取会员信息
        Member member = memberService.getUserMember(user.getId());
        
        // 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId().toString());

        // 构建返回结果
        return buildLoginVO(user, member, token);
    }

    @Override
    public LoginVO passwordLogin(PasswordLoginDTO passwordLoginDTO) {
        String phone = passwordLoginDTO.getPhone();
        String password = passwordLoginDTO.getPassword();
        
        // 检查账号是否被锁定
        String lockKey = LOGIN_LOCK_PREFIX + phone;
        if (redisTemplate.hasKey(lockKey)) {
            throw new BusinessException("登录次数过多，休息15分钟再试～");
        }

        // 查询用户
        User user = userMapper.selectByPhone(phone);
        if (user == null) {
            throw new BusinessException("用户不存在，请先注册");
        }

        // 验证密码
        if (!PasswordUtils.matches(password, user.getPassword())) {
            // 密码错误，记录错误次数
            String errorCountKey = LOGIN_ERROR_COUNT_PREFIX + phone;
            Integer errorCount = (Integer) redisTemplate.opsForValue().get(errorCountKey);
            if (errorCount == null) {
                errorCount = 1;
            } else {
                errorCount++;
            }
            
            // 如果错误次数达到限制，锁定账号
            if (errorCount >= LOGIN_ERROR_LIMIT) {
                redisTemplate.opsForValue().set(lockKey, 1, LOGIN_LOCK_TIME, TimeUnit.SECONDS);
                redisTemplate.delete(errorCountKey);
                throw new BusinessException("登录次数过多，休息15分钟再试～");
            }
            
            // 更新错误次数
            redisTemplate.opsForValue().set(errorCountKey, errorCount, 10, TimeUnit.MINUTES);
            throw new BusinessException("密码错误，请重新输入");
        }

        // 登录成功，清除错误次数记录
        String errorCountKey = LOGIN_ERROR_COUNT_PREFIX + phone;
        redisTemplate.delete(errorCountKey);

        // 获取会员信息
        Member member = memberService.getUserMember(user.getId());
        
        // 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId().toString());

        // 构建返回结果
        return buildLoginVO(user, member, token);
    }

    @Transactional
    @Override
    public LoginVO resetPassword(ResetPasswordDTO resetPasswordDTO) {
        String phone = resetPasswordDTO.getPhone();
        String code = resetPasswordDTO.getCode();
        String newPassword = resetPasswordDTO.getNewPassword();
        
        // 验证验证码
        String cacheKey = SMS_CODE_RESET_PREFIX + phone;
        String cachedCode = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cachedCode == null) {
            throw new BusinessException("验证码已过期，请重新获取");
        }
        if (!code.equals(cachedCode)) {
            throw new BusinessException("验证码错误，请重新输入");
        }

        // 查询用户
        User user = userMapper.selectByPhone(phone);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 更新密码
        user.setPassword(PasswordUtils.encode(newPassword));
        user.setUpdateTime(new Date());
        userMapper.updateById(user);

        // 清除验证码
        redisTemplate.delete(cacheKey);

        // 获取会员信息
        Member member = memberService.getUserMember(user.getId());
        
        // 生成JWT令牌（重置密码后自动登录）
        String token = jwtUtils.generateToken(user.getId().toString());

        // 构建返回结果
        return buildLoginVO(user, member, token);
    }

    @Override
    public void sendBindCode(SendCodeDTO sendCodeDTO) {
        String phone = sendCodeDTO.getPhone();
        String cacheKey = SMS_CODE_BIND_PREFIX + phone;

        // 检查是否在60秒内已发送过验证码
        if (redisTemplate.hasKey(cacheKey)) {
            throw new BusinessException("验证码已发送，请60秒后重试");
        }

        // 检查手机号是否已被其他用户绑定
        User existingUser = userMapper.selectByPhone(phone);
        if (existingUser != null) {
            throw new BusinessException("该手机号已被其他账号绑定");
        }

        // 生成4位数字验证码
        String code = generateSmsCode();
        
        // 缓存验证码
        redisTemplate.opsForValue().set(cacheKey, code, SMS_CODE_EXPIRE, TimeUnit.SECONDS);
        
        // 模拟发送验证码
        System.out.println("向手机号 " + phone + " 发送绑定验证码: " + code);
    }

    @Transactional
    @Override
    public LoginVO bindPhone(Long userId, BindPhoneDTO bindPhoneDTO) {
        String phone = bindPhoneDTO.getPhone();
        String code = bindPhoneDTO.getCode();
        String password = bindPhoneDTO.getPassword();
        
        // 验证验证码
        String cacheKey = SMS_CODE_BIND_PREFIX + phone;
        String cachedCode = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cachedCode == null) {
            throw new BusinessException("验证码已过期，请重新获取");
        }
        if (!code.equals(cachedCode)) {
            throw new BusinessException("验证码错误，请重新输入");
        }

        // 查询用户
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 检查手机号是否已被其他用户绑定
        User existingUser = userMapper.selectByPhone(phone);
        if (existingUser != null && !existingUser.getId().equals(userId)) {
            throw new BusinessException("该手机号已被其他账号绑定");
        }

        // 检查用户是否已绑定手机号
        if (user.getPhone() != null && !user.getPhone().equals(phone)) {
            throw new BusinessException("当前账号已绑定其他手机号");
        }

        // 更新用户信息
        user.setPhone(phone);
        user.setPassword(PasswordUtils.encode(password));
        user.setUpdateTime(new Date());
        userMapper.updateById(user);

        // 清除验证码
        redisTemplate.delete(cacheKey);

        // 获取会员信息
        Member member = memberService.getUserMember(user.getId());
        
        // 生成JWT令牌
        String token = jwtUtils.generateToken(user.getId().toString());

        // 构建返回结果
        return buildLoginVO(user, member, token);
    }

    @Override
    public LoginVO refreshToken(String oldToken) {
        // 验证旧令牌是否有效
        if (!jwtUtils.validateToken(oldToken)) {
            throw new BusinessException("令牌已过期或无效");
        }

        // 检查令牌是否在黑名单中
        String blacklistKey = TOKEN_BLACKLIST_PREFIX + oldToken;
        Boolean isBlacklisted = redisTemplate.hasKey(blacklistKey);
        if (Boolean.TRUE.equals(isBlacklisted)) {
            throw new BusinessException("令牌已被撤销");
        }

        // 从旧令牌中获取用户ID
        Long userId = jwtUtils.getUserIdFromTokenAsLong(oldToken);
        if (userId == null) {
            throw new BusinessException("无效的令牌，请重新登录");
        }
        
        // 查询用户是否存在
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        // 将旧令牌加入黑名单
        long expireTime = jwtUtils.getExpireTimeFromToken(oldToken) - System.currentTimeMillis();
        if (expireTime > 0) {
            redisTemplate.opsForValue().set(blacklistKey, "1", expireTime, TimeUnit.MILLISECONDS);
        }

        // 获取会员信息
        Member member = memberService.getUserMember(userId);
        
        // 生成新的JWT令牌（7天有效期）
        String newToken = jwtUtils.generateTokenWithExpiration(userId.toString(), REFRESH_TOKEN_EXPIRE);

        // 构建返回结果
        return buildLoginVO(user, member, newToken);
    }

    /**
     * 检查设备验证
     */
    private void checkDeviceVerification(Long userId, String deviceInfo) {
        String deviceKey = DEVICE_VERIFY_PREFIX + userId;
        String cachedDeviceInfo = (String) redisTemplate.opsForValue().get(deviceKey);
        
        // 如果是新设备，记录设备信息
        if (cachedDeviceInfo == null) {
            redisTemplate.opsForValue().set(deviceKey, deviceInfo, DEVICE_VERIFY_EXPIRE, TimeUnit.SECONDS);
        }
        // 如果设备信息不匹配，模拟触发二次验证（实际项目中应该发送验证短信）
        else if (!cachedDeviceInfo.equals(deviceInfo)) {
            System.out.println("检测到新设备登录，需要二次验证");
            // 这里只是记录，实际项目中应该实现二次验证逻辑
        }
    }

    /**
     * 模拟第三方授权
     */
    private String simulateThirdAuth(String code, String platform) {
        // 实际项目中应该调用第三方平台的API进行授权验证
        // 这里只是简单模拟，生成一个唯一标识
        String cacheKey = THIRD_LOGIN_CACHE_PREFIX + platform + ":" + code;
        String openId = (String) redisTemplate.opsForValue().get(cacheKey);
        
        if (openId == null) {
            // 生成唯一的openId
            openId = platform + "_" + UUID.randomUUID().toString().substring(0, 8);
            redisTemplate.opsForValue().set(cacheKey, openId, THIRD_LOGIN_EXPIRE, TimeUnit.SECONDS);
        }
        
        return openId;
    }

    /**
     * 根据第三方登录信息查询用户
     */
    private User findUserByThirdKey(String thirdKey) {
        // 实际项目中应该根据thirdKey查询用户，这里简化处理
        // 假设通过phone查询
        return null;
    }

    /**
     * 根据第三方登录创建用户
     */
    private User createUserByThirdLogin(String platform, String openId) {
        User user = new User();
        user.setPhone(platform.toLowerCase() + "_" + openId.substring(0, 6));
        user.setNickname(platform.equals("WECHAT") ? "微信用户" : "支付宝用户");
        user.setAvatar("default_avatar.png");
        user.setStatus(1);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        userMapper.insert(user);
        return user;
    }

    /**
     * 构建登录响应对象
     */
    private LoginVO buildLoginVO(User user, Member member, String token) {
        LoginVO loginVO = new LoginVO();
        loginVO.setUserId(user.getId());
        loginVO.setPhone(user.getPhone());
        loginVO.setToken(token);
        
        LoginVO.MemberInfo memberInfo = new LoginVO.MemberInfo();
        if (member != null) {
            memberInfo.setMemberType(member.getMemberType());
            memberInfo.setExpireTime(member.getExpireTime());
            memberInfo.setIsValid(member.getExpireTime() != null && member.getExpireTime().after(new Date()));
        } else {
            memberInfo.setMemberType(0);
            memberInfo.setExpireTime(null);
            memberInfo.setIsValid(false);
        }
        loginVO.setMemberInfo(memberInfo);

        return loginVO;
    }

    /**
     * 生成4位数字验证码
     */
    private String generateSmsCode() {
        Random random = new Random();
        int code = 1000 + random.nextInt(9000); // 生成1000-9999的随机数
        return String.valueOf(code);
    }
}